import { ref, Ref, watch } from "vue";
import {RouteLocationNormalizedLoaded, Router, RouteRecordRaw} from "vue-router";

export function templateReplace(templateStr:string,param:Record<string,any>):string {
    for(const [key,value] of Object.entries(param)) {
        templateStr = templateStr.replaceAll(`:${key}`,`${value}`);
    }
    return templateStr;
}
const instanceCache:Map<any,Map<string,any>> = new Map();
export abstract class Instantiation {
    public static getInstance<T>(this:{new(...args:any[]):T,instanceHash:(...args:any[])=>string},...args:any[]):T {
        const hash = this.instanceHash(args);
        let instance:T;
        if(instanceCache.has(this)){
            const argsMap = instanceCache.get(this)!;
            if(argsMap.has(hash)){
                instance = argsMap.get(hash)
            }else {
                instance = new this(...args);
                argsMap.set(hash,instance);
            }
        } else {
            instance = new this(...args);
            const argsMap = new Map();
            argsMap.set(hash,instance);
            instanceCache.set(this,argsMap);
        }
        return instance;
    }
    public static instanceHash(...args:any[]):string {
        return JSON.stringify(args);
    }
}

export function getRouterPath(routerName:string|Symbol,root?:RouteRecordRaw|null,path:RouteRecordRaw[] = []):RouteRecordRaw[]|null {
    if(!root)return null;
    if(root.name === routerName)return [...path,root];
    if(!root.children)return null;
    for(const child of root.children) {
        const target = getRouterPath(routerName,child,[...path,root]);
        if(target)return target
    }
    return null;
}

export function findRouteRecordInArray(routeName:string|Symbol,rootArray:Array<RouteRecordRaw>):RouteRecordRaw|null {
    for(const root of rootArray){
        const target = findRouteRecord(routeName,root);
        if(target)return target;
    }
    return null;
}

export function findRouteRecord(routeName:string|Symbol,root?:RouteRecordRaw):RouteRecordRaw|null {
    if(!root)return null;
    if(root.name === routeName)return root;
    if(!root.children)return null;
    for(const child of root.children) {
        const target = findRouteRecord(routeName,child);
        if(target)return target;
    }
    return null;
}

export function useRouterPath(route:Ref<RouteLocationNormalizedLoaded>,root?:RouteRecordRaw|null) {
    const routePath:Ref<RouteRecordRaw[]> = ref([])

    watch(route,(current)=>{
        const name = current.name;
        if(!name)return;
        const path = getRouterPath(name,root);
        if(!path)return;
        routePath.value = path;
    },{immediate:true})


    return {
        routePath
    }
}

export function useRouterTab(route:Router,rootRoute:RouteRecordRaw,routes:Ref<RouteRecordRaw[]>) {
    const activeName = ref("")
    const tabPanels = ref<{name:string,title:string,route:RouteRecordRaw}[]>([])
    let skipRouteChange = false;
    watch(routes,(current)=>{

        tabPanels.value = current.map((v)=>({
            name:String(v.name),
            title:`${v.meta?.title??v.name}`,
            route:v
        }))
    },{immediate:true})
    watch(route.currentRoute,(current)=>{
        if(tabPanels.value.length === 0)return
        if(!current.name)return
        if(skipRouteChange){
            skipRouteChange = false;
            return
        }
        activeName.value = tabPanels.value[0].name

        for(const item of tabPanels.value){
            const record = findRouteRecord(current.name,item.route as RouteRecordRaw)
            if(record) {
                activeName.value = item.name
            }
        }
    },{immediate:true})
    watch(activeName,(currentName)=>{
        const res = tabPanels.value.find((v)=>v.name === currentName);
        if(!res)return;
        if(!route.currentRoute.value.name)return;
        if(!findRouteRecord(route.currentRoute.value.name,rootRoute))return
        skipRouteChange = true;
        route.push({
            name:res.name
        }).then()
    })
    return {
        activeName,
        tabPanels
    }
}
export function useLoadingStatus<T extends (...args:any[])=>any,P extends Parameters<T>,R extends ReturnType<T>>(func:T) {
    const loading = ref(false)
    function loadingFunc(...param:P):R {
        loading.value = true;
        
        try {
            const res = func(param)
            if(res instanceof Promise){
                res.finally(()=>{
                    loading.value = false;
                })
            }
            return res
        }catch(e) {
            throw e;
        }
    }
    return {
        loadingFunc,
        loading
    }
}
